package com.example.latch;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author chentao
 * @version 1.0
 * @description: 基于数据库的分布式锁实现
 * @date 2025/2/28 下午11:59
 */
public class Latch {

    Logger logger = LoggerFactory.getLogger(getClass());

    private LatchRun latchRun;

    private volatile boolean isRunning = true;

    private Connection connection;

    private boolean connAutoCommit = false;

    private PreparedStatement preparedStatement;

    private String sql = "select * from sys_lock where id = 1 for update";

    private String selectSql = "select 1";

    private ScheduledExecutorService scheduledExecutorService;

    public Latch(Connection connection, LatchRun latchRun) {
        this.connection = connection;
        this.latchRun = latchRun;
        this.scheduledExecutorService = Executors.newScheduledThreadPool(10);
    }

    /****
     * 获取锁并执行业务操作
     * @throws Exception
     */
    public void lock() throws Exception {
        this.scheduledExecutorService.execute(new Runnable() {
            @Override
            public void run() {
                while (isRunning) {
                    try {
                        connAutoCommit = connection.getAutoCommit();
                        connection.setAutoCommit(false);
                        preparedStatement = connection.prepareStatement(sql);
                        preparedStatement.execute();
                        break;
                    } catch (Exception e) {
                        logger.info("lock failed: " + e.getMessage());
                    } finally {
                        if (preparedStatement != null) {
                            try {
                                preparedStatement.close();
                            } catch (SQLException e) {
                                logger.warn("preparedStatement close failed", e);
                            }
                        }
                    }
                }
                while (isRunning) {
                    latchRun.run();
                }
            }
        });
        this.scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                PreparedStatement preparedStatement = null;
                try {
                    preparedStatement = connection.prepareStatement(selectSql);
                    preparedStatement.executeQuery();
                } catch (Exception e) {
                    logger.warn("preparedStatement execute failed", e);
                } finally {
                    if (preparedStatement != null) {
                        try {
                            preparedStatement.close();
                        } catch (SQLException e) {
                            logger.warn("preparedStatement close failed", e);
                        }
                    }
                }
            }
        }, 0, 10, TimeUnit.SECONDS);
    }

    /****
     * 释放锁并关闭连接
     * @throws Exception
     */
    public void unlock() {
        this.isRunning = false;
        if (connection != null) {
            try {
                connection.commit();
            } catch (SQLException e) {
                logger.warn("connection commit error: " + e.getMessage());
            }
        }
        try {
            if (connection != null) {
                connection.setAutoCommit(connAutoCommit);
            }
        } catch (SQLException e) {
            logger.warn("connection set autocommit error: " + e.getMessage());
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                logger.warn("connection close error: " + e.getMessage());
            }
        }
        if (null != preparedStatement) {
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                logger.warn("preparedStatement close error: " + e.getMessage());
            }
        }
        this.scheduledExecutorService.shutdownNow();
    }
}
